pub mod todo;
pub mod history;
pub mod indicators;
pub mod kline;
pub mod malines;
pub mod security;
pub mod table;
pub mod trade;
pub mod tick;

use anyhow::Result;
use anyhow::anyhow;
use log::error;
use once_cell::sync::Lazy;
use surrealdb::engine::remote::ws::Client;
use surrealdb::engine::remote::ws::Ws;
use surrealdb::opt::auth::Root;
use surrealdb::Surreal;

// create an HLC with a random u128 and relying on SystemTime::now()
pub static FUHLC: Lazy<uhlc::HLC> = Lazy::new(|| {
    const ID_BYTES: [u8; 3] = [0x01, 0x02, 0x03];

    // https://crates.io/crates/uhlc

    let custom_id = uhlc::ID::try_from(ID_BYTES).unwrap();
    // let my_custom_gps_clock = MyCustomGPSClock::new(); // Initialize your cust/om GPS clock here

    uhlc::HLCBuilder
        ::new()
        .with_id(custom_id)
        // .with_clock(my_custom_gps_clock)
        .with_max_delta(core::time::Duration::from_secs(1))
        .build()
});

pub static FUDB: Lazy<Surreal<Client>> = Lazy::new(Surreal::init);
/// init database for testing
pub async fn init() -> Result<()> {
    init_with("127.0.0.1", 8000, "root", "root", "test", "test").await
}

pub async fn init2(ip: &str, port: u16) -> Result<()> {
    // sync ------- syns

    // syns:
    // ❯ surreal start  --user root --pass root --bind 0.0.0.0:8000 --allow-all  file:./surreal.db
    // ❯ surreal sql --endpoint http://localhost:8000 --username root --password root --namespace test --database test
    // sync:
    // ❯ surreal start  --user root --pass root --bind 0.0.0.0:8800 --allow-all  file:./sync.db
    // ❯ surreal sql --endpoint http://localhost:8800 --username root --password root --namespace test --database test

    init_with(ip, port, "root", "root", "test", "test").await
}

/// init database with parameters
///
/// ip: e.g. 127.0.0.1
///
/// port: e.g. 8000
///
/// username: e.g. root
///
/// password: e.g. pass
///
/// namespace: e.g. test
///
/// dbname: e.g. test
///
pub async fn init_with(
    ip: &str,
    port: u16,
    username: &str,
    password: &str,
    namespace: &str,
    dbname: &str
) -> Result<()> {
    // Connect to the database
    let connect_rs = FUDB.connect::<Ws>(format!("{ip}:{port}")).await;
    match connect_rs {
        Ok(_) => {
            // Signin as a namespace, database, or root user
            let signin_rs = FUDB.signin(Root {
                username: &format!("{username}"),
                password: &format!("{password}"),
            }).await;

            match signin_rs {
                Ok(_) => {
                    // Select a namespace + database
                    let usedb_rs = FUDB.use_ns(&format!("{namespace}")).use_db(
                        &format!("{dbname}")
                    ).await;
                    match usedb_rs {
                        Ok(_) => {
                            // Define the unique index on the kline_of table, Failed to parse query IF NOT EXISTS
                            // you had better to define index manually outside , not in the code
                            // let _ = FUDB
                            //     .query(r#"
                            //          DEFINE INDEX idx_kline_security_id ON TABLE kline COLUMNS security_id; // this is very important
                            //          DEFINE INDEX idx_kline_security_id_klt_klts ON TABLE kline COLUMNS security_id, klt, klts UNIQUE;
                            //          DEFINE INDEX idx_kline_security_klt ON TABLE kline COLUMNS security_id, klt;
                            //          DEFINE INDEX idx_kline_klt ON TABLE kline COLUMNS klt;
                            //          DEFINE INDEX idx_kline_klts ON TABLE kline COLUMNS klts;
                            //      "#)
                            //     .await?;

                            // let _ = FUDB
                            //     .query(r#"
                            //          DEFINE INDEX idx_klsum_klts ON TABLE kline_summary COLUMNS klts;
                            //          DEFINE INDEX idx_klsum_security_id ON TABLE kline_summary COLUMNS security_id;
                            // "#)
                            //     .await?;

                            // let _ = FUDB
                            //     .query(r#"
                            //          DEFINE INDEX idx_todo_status ON TABLE todo COLUMNS status;
                            //          DEFINE INDEX idx_todo_types_status_plan_at ON TABLE todo COLUMNS type_,status,plan_at;
                            // "#)
                            //     .await?;


                            // let _ = FUDB
                            //     .query(r#"
                            //          DEFINE INDEX idx_security_market_type ON TABLE security COLUMNS market,type_;
                            //          DEFINE INDEX idx_security_market ON TABLE security COLUMNS market;
                            // "#)
                            //     .await?;

                            // let _ = FUDB
                            //     .query(r#"DEFINE INDEX idx_column_of ON TABLE column_of COLUMNS in, out UNIQUE;"#)
                            //     .await?;
                            // let _ = FUDB
                            //     .query(r#"DEFINE INDEX idx_trade_bot_with ON TABLE trade_bot_with COLUMNS in, out UNIQUE;"#)
                            //     .await?;
                        }
                        Err(e) => {
                            error!("Failed DB: {:?} with ip={} port={}", e, ip, port);
                            return Err(anyhow!(e));
                        }
                    }
                }
                Err(e) => {
                    error!("Failed DB: {:?} with ip={} port={}", e, ip, port);
                    return Err(anyhow!(e));
                }
            }
        }
        Err(e) => {
            error!("Failed DB: {:?} with ip={} port={}", e, ip, port);
            return Err(anyhow!(e));
        }
    }

    Ok(())
}
